﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using AnimationPackerEngine.Animation;
using AnimationPackerEngine.Exporters;
using AnimationPackerEngine.Packing;

namespace AnimationPackerEngine
{
    public struct ProcessParams
    {
        public int MaxWidth { get; set; }
        public int MaxHeight { get; set; }
        public int Padding { get; set; }
        public bool Pow2 { get; set; }
        public bool Square { get; set; }
        public string SpriteAtlasImage { get; set; }
        public string SpriteAtlasMap { get; set; }
        public string SpriteAtlasIndex { get; set; }
    };

    public class Worker
    {
        public static ProcessingAnimationCollection GetAnimationsFromDirectory(string directory, IPackingStatus status)
        {
            AnimationCompiler animationCompiler = new AnimationCompiler(directory, status);
            if (animationCompiler.Ignore)
            {
                return null;
            }

            ProcessingAnimationCollection animationCollection = new ProcessingAnimationCollection();

            List<string> imageFilenames = new List<string>();
            imageFilenames.AddRange(MiscHelper.AllowedImageFilter.Split('|')
                .SelectMany(filter => Directory.EnumerateFiles(directory, filter)));

            ProcessingAnimation myAnimation = new ProcessingAnimation(directory);

            foreach (string filename in imageFilenames)
            {
                Bitmap bitmap = Bitmap.FromFile(filename) as Bitmap;

                if (bitmap != null)
                {
                    ProcessingFrame frame = new ProcessingFrame();
                    frame.Bitmap = bitmap;
                    frame.Filename = Path.GetFileNameWithoutExtension(filename);
                    frame.Size = frame.Bitmap.Size;

                    myAnimation.Frames.Add(frame);
                }
            }

            if (myAnimation.Frames.Count > 0)
            {
                myAnimation.AnimationCompiler = animationCompiler;
                animationCollection.AnimationCollection.Add(myAnimation);
            }

            List<string> subdirs = new List<string>();
            subdirs.AddRange(Directory.EnumerateDirectories(directory));

            foreach (string subdir in subdirs)
            {
                ProcessingAnimationCollection subdirCollection = GetAnimationsFromDirectory(subdir, status);
                if (subdirCollection != null)
                {
                    animationCollection.AnimationCollection.AddRange(subdirCollection.AnimationCollection);
                }
            }

            return animationCollection;
        }

        public static bool ProcessAnimations(ProcessingAnimationCollection animationCollection, ProcessParams compileParams, IPackingProgressDisplay progressDisplay, IPackingStatus status)
        {
            if (animationCollection.AnimationCollection.Count == 0)
            {
                status.Error("No animations to process.");
                return false;
            }

            // make sure we have our list of exporters
            ExporterCollection.Load();

            // try to find matching exporters
            IImageExporter imageExporter = ExporterCollection.ImageExporterForFilename(compileParams.SpriteAtlasImage);
            IMapExporter mapExporter = ExporterCollection.MapExporterForFilename(compileParams.SpriteAtlasMap);
            IAtlasIndexExporter indexExporter = ExporterCollection.IndexExporterForFilename(compileParams.SpriteAtlasIndex);

            if (imageExporter == null)
            {
                status.Fatal("Failed to find exporter for image file: " + compileParams.SpriteAtlasImage);
                return false;
            }

            // generate our output
            ImagePacker imagePacker = new ImagePacker(progressDisplay);
            Bitmap outputImage;

            // pack the image, generating a map only if desired
            if (!imagePacker.PackImage(compileParams, animationCollection.AnimationCollection, out outputImage, status))
            {
                return false;
            }

            // try to save using our exporters
            try
            {
                if (File.Exists(compileParams.SpriteAtlasImage))
                {
                    File.Delete(compileParams.SpriteAtlasImage);
                }

                imageExporter.Save(compileParams.SpriteAtlasImage, outputImage);
            }
            catch (Exception e)
            {
                status.FatalException(string.Format("Error saving {0}: ", compileParams.SpriteAtlasImage), e);
                return false;
            }

            if (mapExporter != null)
            {
                try
                {
                    if (File.Exists(compileParams.SpriteAtlasMap))
                    {
                        File.Delete(compileParams.SpriteAtlasMap);
                    }

                    mapExporter.Save(compileParams.SpriteAtlasMap, animationCollection);
                }
                catch (Exception e)
                {
                    status.FatalException(string.Format("Error saving {0}: ", compileParams.SpriteAtlasMap), e);
                    return false;
                }
            }

            if (indexExporter != null)
            {
                try
                {
                    if (File.Exists(compileParams.SpriteAtlasIndex))
                    {
                        File.Delete(compileParams.SpriteAtlasIndex);
                    }

                    indexExporter.Save(compileParams.SpriteAtlasIndex, animationCollection);
                }
                catch (Exception e)
                {
                    status.FatalException(string.Format("Error saving {0}: ", compileParams.SpriteAtlasIndex), e);
                    return false;
                }
            }

            return true;
        }

        public static bool CompileAnimations(string rootDirectory, ProcessParams compileParams, IPackingProgressDisplay progressDisplay, IPackingStatus status)
        {
            ProcessingAnimationCollection animations = GetAnimationsFromDirectory(rootDirectory, status);
            return ProcessAnimations(animations, compileParams, progressDisplay, status);
        }
    }
}
